package com.sc.services.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sc.entity.dto.AdapterDto;
import com.sc.entity.dto.ApiResult;
import com.sc.entity.dto.DeviceDto;
import com.sc.entity.params.DeviceOnlineBatchParam;
import com.sc.entity.params.DeviceOnlineParam;
import com.sc.entity.params.DeviceRegisterParam;
import com.sc.services.IotPlatformService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;

@Service
@Slf4j
public class IotPlatformServiceImpl implements IotPlatformService {
    @Override
    public DeviceDto registerDevice(String url, DeviceRegisterParam param) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");

        CloseableHttpResponse response = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            var json = mapper.writeValueAsString(param);
            StringEntity entity = new StringEntity(json, "UTF-8");
            httpPost.setEntity(entity);

            response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                var body = EntityUtils.toString(response.getEntity());

                var apiResult = mapper.readValue(body, ApiResult.class);

                if (apiResult.getCode() != 200) {
                    log.warn("Error returned code {}", apiResult.getCode());
                    return null;
                }

                if (apiResult.getData() == null) {
                    log.warn("No data found in ApiResult");
                    return null;
                }

                var deviceMap = apiResult.getData();
                if (deviceMap instanceof LinkedHashMap<?,?> map) {
                    DeviceDto deviceDto = new DeviceDto();

                    var device = (Map<String, Object>) map;
                    BeanUtils.populate(deviceDto, device);
                    return deviceDto;
                }
            }
        } catch (Exception e) {
            log.warn("RegisterDevice: {}", e.getMessage());

        }
        return null;
    }

    @Override
    public Boolean sendOnline(String url, String token, DeviceOnlineParam param) {
        String onlineUrl = url + "/" + token + "/online";
        HttpPost httpPost = new HttpPost(onlineUrl);
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");

        return DoSendOnline(param, httpPost);
    }

    private Boolean DoSendOnline(Object param, HttpPost httpPost) {
        CloseableHttpResponse response;

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            ObjectMapper mapper = new ObjectMapper();
            var json = mapper.writeValueAsString(param);

            StringEntity entity = new StringEntity(json, "UTF-8");
            httpPost.setEntity(entity);

            log.info("Send batch devices to {}", httpPost.getURI().toString());
            response = httpClient.execute(httpPost);
            if (response.getStatusLine().getStatusCode() == 200) {
                var body = EntityUtils.toString(response.getEntity());

                var apiResult = mapper.readValue(body, ApiResult.class);
                if (apiResult.getCode() != 200) {
                    log.warn("Error returned code {}", apiResult.getCode());
                    return false;
                }

                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public Boolean sendOnlineBatch(String url, DeviceOnlineBatchParam param) {
        String onlineUrl = url + "/online-batch";
        HttpPost httpPost = new HttpPost(onlineUrl);
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");

        return DoSendOnline(param, httpPost);
    }

    @Override
    public Boolean heartBeatAsync(String url, AdapterDto dto) {
        String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
        dto.setLastAccessTime(dateStr);

        HttpPost httpPost = new HttpPost(url);
        httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8");

        ObjectMapper mapper = new ObjectMapper();

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            var json = mapper.writeValueAsString(dto);
            StringEntity entity = new StringEntity(json, "UTF-8");
            httpPost.setEntity(entity);

            var resp = httpClient.execute(httpPost);
            return true;
        } catch (Exception e) {
            log.warn("Send heartbeat : " + e.getMessage());
        }
        return false;
    }
}
